package com.waterstudio.filedownloader.ext.m3u8;

import android.support.annotation.FloatRange;
import android.support.annotation.NonNull;
import android.support.annotation.WorkerThread;
import android.text.TextUtils;
import android.util.Log;
import android.util.SparseArray;

import com.liulishuo.filedownloader.BaseDownloadTask;
import com.liulishuo.filedownloader.FileDownloader;
import com.liulishuo.filedownloader.M3U8DownloadTask;
import com.liulishuo.filedownloader.M3u8DownloadListenerWrapper;
import com.download.database.TsDatabase;
import com.download.database.TsKey;
import com.download.database.TsTask;
import javax.exception.TsNotFoundException;
import com.liulishuo.filedownloader.model.FileDownloadStatus;
import com.liulishuo.filedownloader.util.FileDownloadUtils;
import com.waterstudio.filedownloader.ext.m3u8.bean.EncKey;
import com.waterstudio.filedownloader.ext.m3u8.bean.M3U8;
import com.waterstudio.filedownloader.ext.m3u8.bean.M3U8Ts;
import com.waterstudio.filedownloader.ext.m3u8.bean.MasterList;
import com.waterstudio.filedownloader.ext.m3u8.utils.FileUtils;
import com.waterstudio.filedownloader.ext.m3u8.utils.M3u8Parser;
import com.waterstudio.filedownloader.ext.m3u8.utils.MUtils;

import org.apache.commons.io.IOUtils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * <p>
 * Created by chensuilun on 2019/3/11.
 */
public class M3u8FileDownloader implements M3u8DownloadCallback {
    private static final String TAG = "M3u8FileDownloader";

    private int mThreadNum = 2;
    /**
     * 激进的方式来合并 ts 文件，边合并边删除，可以节省手机内存空间，否则需要花费两倍的存储空间
     */
    private boolean mIsRadicalMerge = false;

    private static M3u8FileDownloader sInstance;

    private final Object lock = new Object();

    private static AtomicBoolean sFirstInit = new AtomicBoolean(false);

    private final static Executor TS_DB_EXECUTOR = Executors.newSingleThreadExecutor(r -> new Thread(r, "TS_SYNC"));

    private SparseArray<List<TsTask>> mTsTask = new SparseArray<>();
    private SparseArray<M3u8DownloadListenerWrapper> mTaskArray = new SparseArray<>();
    private SparseArray<AtomicInteger> mTsCountdown = new SparseArray<>();
    private SparseArray<SparseArray<TsDownloadHandler>> mTaskHandler = new SparseArray<>();

    public synchronized static M3u8FileDownloader getInstance() {
        if (sInstance == null) {
            sInstance = new M3u8FileDownloader();
        }
        return sInstance;
    }

    private void setThreadNum(int threadNum) {
        mThreadNum = threadNum;
    }

    private void setRadicalMerge(boolean radicalMerge) {
        mIsRadicalMerge = radicalMerge;
    }

    public synchronized static void init(Builder builder) {
        if (sFirstInit.get()) {
            if (BuildConfig.LOG_ENABLE) {
                Log.w(TAG, "init call duplicate,ignore the second call.");
            }
            return;
        }
        sFirstInit.compareAndSet(false, true);
        getInstance().setRadicalMerge(builder.radicalMerge);
        getInstance().setThreadNum(builder.threadSize);
    }


    public static final class Builder {
        private int threadSize;
        private boolean radicalMerge;

        public Builder() {
        }

        public Builder setThreadSize(int threadSize) {
            this.threadSize = threadSize;
            return this;
        }

        public Builder setRadicalMerge(boolean radicalMerge) {
            this.radicalMerge = radicalMerge;
            return this;
        }
    }


    private M3u8FileDownloader() {

    }

    public BaseDownloadTask create(@NonNull String url) {
        if (!MUtils.isM3u8Url(url)) {
            if (BuildConfig.LOG_ENABLE) {
                Log.w(TAG, "call makeM3u8Request with not m3u8 url " + url + " ,just ignore.");
            }
            return FileDownloader.getImpl().create(url);
        }
        M3U8DownloadTask task = new M3U8DownloadTask(url, this);
        task.setTag(R.id.is_m3u8_task, true);
        return task;
    }


    public byte getStatus(final String url, final String path) {
        int parentId = FileDownloadUtils.generateId(url, path);
        //1.target file exist?
        String mergePath = MUtils.getM3u8TsMergePath(path);
        if (new File(mergePath).exists()) {
            return FileDownloadStatus.completed;
        }
        M3u8DownloadListenerWrapper listenerWrapper = mTaskArray.get(parentId);
        if (listenerWrapper != null) {
            //2.get the group status
            return listenerWrapper.getStatus();
        } else {
            return FileDownloadStatus.INVALID_STATUS;
        }
    }


    /**
     */
    public void pause(final int m3u8Id) {
        if (BuildConfig.LOG_ENABLE) {
            Log.d(TAG, "pause() called with: id" + m3u8Id);
        }
        FileDownloader.getImpl().pause(m3u8Id);
        M3u8DownloadListenerWrapper listenerWrapper = mTaskArray.get(m3u8Id);
        if (listenerWrapper != null) {
            if (listenerWrapper.getStatus() != FileDownloadStatus.completed
                    && listenerWrapper.getStatus() != FileDownloadStatus.blockComplete
                    && listenerWrapper.getStatus() != FileDownloadStatus.warn
                    && listenerWrapper.getStatus() != FileDownloadStatus.error) {
                SparseArray<TsDownloadHandler> handlerSparseArray = mTaskHandler.get(m3u8Id, null);
                TsDownloadHandler tsDownloadHandler;
                if (handlerSparseArray != null) {
                    for (int i = 0; i < handlerSparseArray.size(); i++) {
                        tsDownloadHandler = handlerSparseArray.get(handlerSparseArray.keyAt(i));
                        tsDownloadHandler.pause();
                    }
                }
                mTaskHandler.put(m3u8Id, null);
                FileDownloader.getImpl().pause(listenerWrapper);
                mTsCountdown.put(m3u8Id, null);
            }
        }
    }

    /**
     * @param url
     * @param path
     */
    public void pause(final String url, final String path) {
        int parentId = FileDownloadUtils.generateId(url, path);
        this.pause(parentId);
    }

    /**
     * @param url
     * @param path
     */
    public void clearData(final String url, final String path) {
        int parentId = FileDownloadUtils.generateId(url, path);
        pause(url, path);
        FileDownloader.getImpl().clear(parentId, path);
        deleteFile(path);
        deleteFile(MUtils.getM3u8TsDir(path));
        deleteFile(MUtils.getM3u8TsMergePath(path));
        mTaskArray.put(parentId, null);
        mTsTask.put(parentId, null);
        TS_DB_EXECUTOR.execute(() -> {
            //多线程问题。如果在删除的同时启动相同的任务，那么如果先插入了数据将会被删除。
            List<TsTask> taskList = TsDatabase.getInstance().tsDao().getAllTsTask(parentId);
            TsDatabase.getInstance().tsDao().deleteAll(taskList);
        });
    }

    private void deleteFile(String path) {
        FileUtils.delFile(path);
    }


    /**
     * @param url  m3u8  url
     * @param path save path of m3u8 file
     * @return current progress
     */
    @WorkerThread
    public float getProgress(final String url, final String path) {
        int parentId = FileDownloadUtils.generateId(url, path);
        //1.判断最终产物是否存在
        if (new File(MUtils.getM3u8TsMergePath(path)).exists()) {
            return 1f;
        }
        //2.判断 db 中的数据
        List<TsTask> taskList = getTsTask(parentId);
        if (taskList != null && !taskList.isEmpty()) {
            long total = 0, soFar = 0;
            for (TsTask ts : taskList) {
                total += ts.getDuration();
                soFar += ts.getCurDuration();
            }
            if (total != 0) {
                return soFar / (float) total;
            }
            return 0;
        }
        //3. 判读 m3u8 文件是否存在
        return 0;
    }

    private List<TsTask> getTsTask(int m3u8Id) {
        synchronized (lock) {
            List<TsTask> list = mTsTask.get(m3u8Id);
            if (list != null) {
                return list;
            } else {
                list = TsDatabase.getInstance().tsDao().getAllTsTask(m3u8Id);
                mTsTask.put(m3u8Id, list);
            }
            return list;
        }
    }


    public boolean isM3u8Task(BaseDownloadTask task) {
        Object m3u8 = task.getTag(R.id.is_m3u8_task);
        return (m3u8 instanceof Boolean) && (Boolean) m3u8;
    }

    public boolean isM3u8TsTask(BaseDownloadTask task) {
        Object m3u8Ts = task.getTag(R.id.is_m3u8_ts_task);
        return (m3u8Ts instanceof Boolean) && (Boolean) m3u8Ts;
    }

    public int getTsIndex(BaseDownloadTask task) {
        Object index = task.getTag(R.id.ts_index);
        return (index instanceof Integer) ? (Integer) index : -1;
    }


    @Override
    public boolean onPendingDownloadM3u8(BaseDownloadTask task, M3u8DownloadListenerWrapper listenerWrapper) {
        int parentId = FileDownloadUtils.generateId(task.getUrl(), task.getTargetFilePath());
        if (BuildConfig.LOG_ENABLE) {
            Log.d(TAG, String.format("InterceptBlockCompleteM3u8:%d, url: %s", parentId, task.getUrl()));
        }
        String mergePath = MUtils.getM3u8TsMergePath(task.getTargetFilePath());
        //1.已下载判断
        if (new File(mergePath).exists() && !task.isForceReDownload()) {
            if (BuildConfig.LOG_ENABLE) {
                Log.w(TAG, String.format("InterceptBlockCompleteM3u8 %s :already downloaded", task.getUrl()));
            }
            task.setTag(R.id.ts_merge_path, mergePath);
            return false;
        }
        if (isRunningOrDone(task)) {
            return false;
        }
        mTaskArray.put(parentId, listenerWrapper);
        return true;
    }

    /**
     * @param task
     * @return 当前任务相关的子任务是否已经完成或者正在下载
     */
    private boolean isRunningOrDone(BaseDownloadTask task) {
        int status = getStatus(task.getUrl(), task.getTargetFilePath());
        if (status > FileDownloadStatus.INVALID_STATUS) {
            return true;
        } else return status == FileDownloadStatus.completed;
    }


    @Override
    public boolean onInterceptBlockCompleteM3u8(BaseDownloadTask task, M3u8DownloadListenerWrapper listenerWrapper) throws Exception {
        int parentId = FileDownloadUtils.generateId(task.getUrl(), task.getTargetFilePath());
        if (BuildConfig.LOG_ENABLE) {
            Log.d(TAG, String.format("onInterceptBlockCompleteM3u8:%d", parentId));
        }
        String mergePath = MUtils.getM3u8TsMergePath(task.getTargetFilePath());
        //1.已下载判断
        if (new File(mergePath).exists() && !task.isForceReDownload()) {
            if (BuildConfig.LOG_ENABLE) {
                Log.w(TAG, String.format("onInterceptBlockCompleteM3u8 %s :already downloaded", task.getUrl()));
            }
            task.setTag(R.id.ts_merge_path, mergePath);
            return false;
        }
        //上一个 M3u8 处于 BlockComplete 耗时流程中？规避再来一个相同的 M3u8 任务直接进入
        int status = getStatus(task.getUrl(), task.getTargetFilePath());
        if (status > FileDownloadStatus.INVALID_STATUS
                && mTaskArray.get(parentId, listenerWrapper) != listenerWrapper) {
            if (BuildConfig.LOG_ENABLE) {
                Log.w(TAG, String.format("onInterceptBlockCompleteM3u8 %s is downloading.", task.getUrl()));
            }
            return false;
        } else if (status == FileDownloadStatus.completed) {
            if (BuildConfig.LOG_ENABLE) {
                Log.w(TAG, String.format("onInterceptBlockCompleteM3u8 %s :already downloaded", task.getUrl()));
            }
            return false;
        }
        mTaskArray.put(parentId, listenerWrapper);
        List<TsTask> tsTaskList = getTsTask(parentId);
        String tsDownloadDir = MUtils.getM3u8TsDir(task.getTargetFilePath());
        if (tsTaskList.isEmpty() || task.isForceReDownload()) {
            M3U8 m3U8 = M3u8Parser.parse(task.getUrl(), new File(task.getTargetFilePath()));
            if (BuildConfig.LOG_ENABLE) {
                Log.v(TAG, "result: " + m3U8);
                Log.i(TAG, "master playlist: " + m3U8.isMasterPlaylist());
            }
            int i = 0;
            String tsSavePath;
            String tsUrl;
            if (m3U8.isMasterPlaylist()) {
                Collections.sort(m3U8.getMasterLists(), (o1, o2) -> o1.getBandWidth() > o2.getBandWidth() ? -1 : 1);
                MasterList masterList = m3U8.getMasterLists().get(0);
                for (M3U8Ts m3U8Ts : masterList.getM3U8().getTsArrayList()) {
                    tsSavePath = tsDownloadDir + File.separator + i + ".ts";
                    tsUrl = m3U8Ts.getUrl();
                    if (TextUtils.isEmpty(tsUrl)) {
                        continue;
                    }
                    if (m3U8Ts.getEncKey() != null) {
                        tsTaskList.add(new TsTask(FileDownloadUtils.generateId(tsUrl, tsSavePath), parentId, i, tsUrl, tsSavePath, (long) (m3U8Ts.getSeconds() * 1000)
                                , new TsKey(m3U8Ts.getEncKey().getKey(), m3U8Ts.getEncKey().getIv())));
                    } else {
                        tsTaskList.add(new TsTask(FileDownloadUtils.generateId(tsUrl, tsSavePath), parentId, i, tsUrl, tsSavePath, (long) (m3U8Ts.getSeconds() * 1000)));
                    }
                    i++;
                }
                TsDatabase.getInstance().tsDao().insertAll(tsTaskList);
            } else {
                for (M3U8Ts m3U8Ts : m3U8.getTsArrayList()) {
                    tsSavePath = tsDownloadDir + File.separator + i + ".ts";
                    tsUrl = m3U8Ts.getUrl();
                    if (TextUtils.isEmpty(tsUrl)) {
                        continue;
                    }
                    if (m3U8Ts.getEncKey() != null) {
                        tsTaskList.add(new TsTask(FileDownloadUtils.generateId(tsUrl, tsSavePath), parentId, i, tsUrl, tsSavePath, (long) (m3U8Ts.getSeconds() * 1000)
                                , new TsKey(m3U8Ts.getEncKey().getKey(), m3U8Ts.getEncKey().getIv())));
                    } else {
                        tsTaskList.add(new TsTask(FileDownloadUtils.generateId(tsUrl, tsSavePath), parentId, i, tsUrl, tsSavePath, (long) (m3U8Ts.getSeconds() * 1000)));
                    }
                    i++;
                }
                TsDatabase.getInstance().tsDao().insertAll(tsTaskList);
            }
        }
        if (!tsTaskList.isEmpty()) {
            SparseArray<TsDownloadHandler> handlersArray = new SparseArray<>(mThreadNum);
            SparseArray<List<TsTask>> splitTask = new SparseArray<>(mThreadNum);
            List<TsTask> list;
            for (int i = 0; i < tsTaskList.size(); i++) {
                list = splitTask.get(i % mThreadNum, new ArrayList<>());
                list.add(tsTaskList.get(i));
                splitTask.put(i % mThreadNum, list);
            }
            int taskSize = 0;
            TsDownloadHandler tsDownloadHandler;
            for (int i = 0; i < mThreadNum; i++) {
                tsDownloadHandler = handlersArray.get(i, TsDownloadHandlerFactory.getTsDownloadHandler(listenerWrapper));
                handlersArray.put(i, tsDownloadHandler);
                taskSize += tsDownloadHandler
                        .prepareDownloadTs(splitTask.get(i % mThreadNum, new ArrayList<>()), task.isForceReDownload());
            }
            mTaskHandler.put(parentId, handlersArray);
            mTsCountdown.put(parentId, new AtomicInteger(taskSize));
            if (BuildConfig.LOG_ENABLE) {
                Log.i(TAG, String.format("start ts download,task size %d", taskSize));
            }
            for (int i = 0; i < mThreadNum; i++) {
                tsDownloadHandler = handlersArray.get(i, null);
                if (tsDownloadHandler != null) {
                    tsDownloadHandler.startDownload();
                }
            }
//            downloadQueueSet.start();
            return true;
        } else {
            if (BuildConfig.LOG_ENABLE) {
                Log.e(TAG, String.format("onInterceptBlockCompleteM3u8 without ts found %s", task.getUrl()));
            }
        }
        throw new TsNotFoundException("Ts not found for m3u8 %s" + task.getUrl());
    }

    @Override
    public boolean onInterceptCompleteM3u8(BaseDownloadTask task) {
        int parentId = FileDownloadUtils.generateId(task.getUrl(), task.getTargetFilePath());
        if (BuildConfig.LOG_ENABLE) {
            Log.d(TAG, String.format("onInterceptCompleteM3u8:%d", parentId));
        }
        //已下载
        if (task.getTag(R.id.ts_merge_path) instanceof String) {
            task.setPath((String) task.getTag(R.id.ts_merge_path), false);
            return false;
        }
        return true;
    }

    @Override
    public void onStartTsDownload(BaseDownloadTask baseDownloadTask, M3u8DownloadListenerWrapper listenerWrapper) {
        //nothing
    }

    @Override
    public @FloatRange(from = 0f, to = 1f)
    float onUpdateProgress(BaseDownloadTask parentTask, BaseDownloadTask tsTask, long soFarBytes, long totalBytes) {
        int parentId = FileDownloadUtils.generateId(parentTask.getUrl(), parentTask.getTargetFilePath());
        AtomicInteger atomicInteger = mTsCountdown.get(parentId);
        if (atomicInteger != null && atomicInteger.get() == -1) {
            if (BuildConfig.LOG_ENABLE) {
                Log.w(TAG, "onUpdateProgress: Task already finish");
            }
        }
        int tsId = FileDownloadUtils.generateId(tsTask.getUrl(), tsTask.getTargetFilePath());
        List<TsTask> tsTasks = mTsTask.get(parentId, null);
        if (tsTasks != null) {
            long total = 0, soFar = 0;
            for (TsTask ts : tsTasks) {
                total += ts.getDuration();
                if (ts.getId() == tsId) {
                    ts.setCurDuration((long) (ts.getDuration() * (float) soFarBytes / (float) totalBytes));
                    TS_DB_EXECUTOR.execute(() -> TsDatabase.getInstance().tsDao().update(ts));
                }
                soFar += ts.getCurDuration();
            }
            if (total != 0) {
                return soFar / (float) total;
            }
        }
        return 0;
    }

    @Override
    public void onTsDownloadPaused(BaseDownloadTask parentTask, BaseDownloadTask tsTask) {
        int parentId = FileDownloadUtils.generateId(parentTask.getUrl(), parentTask.getTargetFilePath());
        if (BuildConfig.LOG_ENABLE) {
            Log.d(TAG, String.format("onTsDownloadPaused:%d, index %d", parentId, getTsIndex(tsTask)));
        }
        mTsCountdown.put(parentId, null);
        mTaskHandler.put(parentId, null);
    }

    @Override
    public void onTsDownloadError(BaseDownloadTask parentTask, BaseDownloadTask tsTask) {
        int parentId = FileDownloadUtils.generateId(parentTask.getUrl(), parentTask.getTargetFilePath());
        if (BuildConfig.LOG_ENABLE) {
            Log.d(TAG, String.format("onTsDownloadError:%d, index %d", parentId, getTsIndex(tsTask)));
        }
        pause(parentId);
        mTsCountdown.put(parentId, null);
        mTaskHandler.put(parentId, null);
    }

    @Override
    public boolean onBlockCompleteTs(BaseDownloadTask parentTask, BaseDownloadTask tsTask) throws Exception {
        int parentId = FileDownloadUtils.generateId(parentTask.getUrl(), parentTask.getTargetFilePath());
        if (BuildConfig.LOG_ENABLE) {
            Log.d(TAG, String.format("onBlockCompleteTs:%d , index %d", parentId, getTsIndex(tsTask)));
        }
        if (tsTask.getTag(R.id.encrypt) instanceof EncKey) {
            EncKey encKey = (EncKey) tsTask.getTag(R.id.encrypt);
            File targetFile = new File(tsTask.getTargetFilePath());
            FileInputStream fileInputStream = new FileInputStream(targetFile);
            FileOutputStream fileOutputStream = new FileOutputStream(targetFile);
            byte[] decrypted;
            try {
                decrypted = encKey.decrypt(IOUtils.readFully(fileInputStream, fileInputStream.available()));
            } finally {
                fileInputStream.close();
            }
            try {
                IOUtils.write(decrypted, fileOutputStream);
            } finally {
                fileOutputStream.close();
            }
        }
        AtomicInteger atomicInteger = mTsCountdown.get(parentId);
        if (atomicInteger != null && atomicInteger.decrementAndGet() == 0) {
            String pathToMerge = MUtils.getM3u8TsMergePath(parentTask.getTargetFilePath());
            if (BuildConfig.LOG_ENABLE) {
                Log.d(TAG, String.format("M3u8 download complete %s, merge path %s", parentTask.getUrl(), pathToMerge));
            }
            String pathToMergeTmp = MUtils.getM3u8TsMergeTmpPath(parentTask.getTargetFilePath());
            List<TsTask> taskList = mTsTask.get(parentId);
            Collections.sort(taskList, (o1, o2) -> o1.getIndex() < o2.getIndex() ? -1 : 1);
            long offset = 0;
            File tmpOut = new File(pathToMergeTmp);
            FileChannel outChannel = new FileOutputStream(tmpOut).getChannel();
            try {
                File tsFile = null;
                FileChannel inChannel = null;
                for (TsTask ts : taskList) {
                    tsFile = new File(ts.getTargetPath());
                    if (!tsFile.exists()) {
                        throw new IllegalStateException(String.format("Ts file miss for %s", ts.getUrl()));
                    }
                    try {
                        inChannel = new FileInputStream(tsFile).getChannel();
                        outChannel.transferFrom(inChannel, offset, inChannel.size());
                        offset += inChannel.size();
                        inChannel.close();
                        if (mIsRadicalMerge) {
                            //避免占用太多空间，但这个过程不能打断，否则需要重新下载
                            deleteFile(ts.getTargetPath());
                        }
                    } finally {
                        if (inChannel != null) {
                            inChannel.close();
                        }
                    }
                }
                if (!tmpOut.renameTo(new File(pathToMerge))) {
                    tmpOut.deleteOnExit();
                    throw new IllegalStateException(String.format("Result %s cannot rename to %s", tmpOut.getAbsolutePath(), pathToMerge));
                }
                if (BuildConfig.LOG_ENABLE) {
                    Log.i(TAG, String.format("M3u8 merge to %s", pathToMerge));
                }
                //delete all ts file
                deleteFile(MUtils.getM3u8TsDir(parentTask.getTargetFilePath()));
            } finally {
                outChannel.close();
            }
            parentTask.setTag(R.id.ts_merge_path, pathToMerge);
            return true;
        } else {
            if (atomicInteger != null) {
                if (BuildConfig.LOG_ENABLE) {
                    Log.i(TAG, String.format("M3u8 download  ts task left ( %d )", atomicInteger.get()));
                }
            } else {
                if (BuildConfig.LOG_ENABLE) {
                    Log.e(TAG, String.format("M3u8 miss ts countdown"));
                }
            }
        }
        return false;
    }

    @Override
    public boolean onCompleteTs(BaseDownloadTask parentTask, BaseDownloadTask tsTask) {
        int parentId = FileDownloadUtils.generateId(parentTask.getUrl(), parentTask.getTargetFilePath());
        if (BuildConfig.LOG_ENABLE) {
            Log.d(TAG, String.format("onCompleteTs:%d, index %d", parentId, getTsIndex(tsTask)));
        }
        AtomicInteger atomicInteger = mTsCountdown.get(parentId);
        if (atomicInteger == null) {
            if (BuildConfig.LOG_ENABLE) {
                Log.e(TAG, String.format("onCompleteTs call with null countdown，may be download complete."));
            }
            return false;
        }
        if (atomicInteger.get() == 0) {
            //prevent call more than one times.
            atomicInteger.compareAndSet(0, -1);
            if (parentTask.getTag(R.id.ts_merge_path) instanceof String) {
                if (BuildConfig.LOG_ENABLE) {
                    Log.e(TAG, String.format("onComplete All %d, target: %s", parentId, parentTask.getTag(R.id.ts_merge_path)));
                }
                parentTask.setPath((String) parentTask.getTag(R.id.ts_merge_path), false);
                mTaskArray.put(parentId, null);
                mTsCountdown.put(parentId, null);
                mTaskHandler.put(parentId, null);
                TS_DB_EXECUTOR.execute(() -> {
                    //多线程问题。如果在删除的同时启动相同的任务，那么如果先插入了数据将会被删除。
                    List<TsTask> taskList = TsDatabase.getInstance().tsDao().getAllTsTask(parentId);
                    TsDatabase.getInstance().tsDao().deleteAll(taskList);
                });
                return true;
            } else {
                throw new IllegalStateException("Cannot found ts merge path with in onCompleteTs callback.");
            }
        }
        return false;
    }

}
